import {
  Action, PermissionsTo, fromParam,
  JwtPayload,
  JwtPayloadDto, PermissionsDecoratorOptions, fromJwt, fromBody,
} from '@boticord/auth'
import { ErrorCodes } from '@boticord/consts'
import { CreateServerDto, Server, UpdateServerDto } from '@boticord/resources-client'
import { User } from '@boticord/users-client'
import {
  Body,
  Controller,
  Delete,
  Get,
  InternalServerErrorException,
  NotFoundException,
  Param,
  Patch,
  Post, Req,
} from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { ServersService } from '../services/servers.service'
import { AnalyseRequest, AnalyticsOptions, EventTypes, getIp } from '@boticord/analytics'
import { FastifyRequest } from 'fastify'

const permissionsOptions: PermissionsDecoratorOptions<Server> = {
  fetch: {
    id: fromParam('id'),
    exception: new NotFoundException({ code: ErrorCodes.UnknownServer }),
    options: {
      relations: {
        moderators: true,
      },
    },
  },
}

const analyticsOptions: AnalyticsOptions = {
  affected: fromParam('id'),
  user: fromJwt('id'),
}

@Controller('servers')
export class ServerController {
  constructor(
    private readonly servers: ServersService,
    @InjectRepository(User) private readonly users: Repository<User>,
  ) {}

  /** get server by id */
  @Get(':id')
  @PermissionsTo(Action.Read, Server, permissionsOptions)
  @AnalyseRequest(EventTypes.ServerViewed, analyticsOptions)
  async getServer(@Param('id') id: string, @JwtPayload() payload?: JwtPayloadDto) {
    const server = await this.servers.get(id)

    if (server === null) {
      throw new NotFoundException({ code: ErrorCodes.UnknownServer })
    }

    return this.servers.get(id, payload?.id)
  }

  /** is service bot on the server or not */
  @Get(':id/service')
  @PermissionsTo(Action.Read, Server)
  @AnalyseRequest(EventTypes.ServiceBotAvailabilityChecked, analyticsOptions)
  async checkServiceBotAvailability(@Param('id') id: string) {
    return await this.servers.checkServiceBotAvailability(id)
  }

  /** delete server */
  @Delete(':id')
  @PermissionsTo(Action.Delete, Server, permissionsOptions)
  @AnalyseRequest(EventTypes.ServerRemoved, analyticsOptions)
  async deleteServer(@Param('id') id: string) {
    await this.servers.delete(id)
    return true
  }

  /** edit server */
  @Patch(':id')
  @PermissionsTo(Action.Modify, Server, permissionsOptions)
  @AnalyseRequest(EventTypes.ServerEdited, analyticsOptions)
  async patchServer(@Param('id') id: string, @Body() dto: UpdateServerDto) {
    return await this.servers.modifyServer(id, dto)
  }

  /** add new server */
  @Post()
  @PermissionsTo(Action.Create, Server)
  @AnalyseRequest(EventTypes.ServerAdded, {
    affected: fromBody('id'),
    user: fromJwt('id'),
  })
  async createServer(@Body() dto: CreateServerDto, @JwtPayload() payload: JwtPayloadDto, @Req() req: FastifyRequest) {
    const ip = getIp(req.headers)
    if (!ip) throw new InternalServerErrorException({ code: ErrorCodes.CannotDetectIp })

    return await this.servers.createServer(dto, payload, ip)
  }

  // TODO: resource status change
}
